// 输出格式化

#ifndef __M_FORMAT_H__
#define __M_FORMAT_H__

#include "message.hpp"
#include <cassert>
#include <memory>
#include <vector>
#include <sstream>

namespace snowdragon
{
    // 格式化项目（功能）基类，派生类为各格式化功能的类
    class FormatItem
    {
    public:
        using ptr = std::shared_ptr<FormatItem>;
        virtual void format(std::ostream &os, const Message &message) = 0;          // 格式化操作
    };
    // 获取时间戳并按照指定格式进行格式化
    class TimeFormatItem : public FormatItem
    {
    public:
        // 通过传参的方式，更好使用
        TimeFormatItem(const std::string &time_format = "%H:%M:%S")
            : _time_format(time_format)
        {
            if (time_format.empty())        // 参数传入了，但什么都没有，使用默认的格式
                _time_format = "%H:%M:%S";
        }
        void format(std::ostream &os, const Message &message)
        {
            // j将时间转化为指定格式再返回，而不是直接返回message._create_time
            struct tm t;
            localtime_r(&message._create_time, &t);
            char buff[32];
            strftime(buff, 31, _time_format.c_str(), &t);           // 将时间转化为字符串存储在buff中
            os << buff;
        }

    private:
        std::string _time_format;       // 指定的时间格式
    };
    // 获取源码所在的文件名
    class FileNameFormatItem : public FormatItem
    {
    public:
        void format(std::ostream &os, const Message &message)
        {
            os << message._file_name;
        }
    };
    // 获取源码所在的行号
    class FileLineFormatItem : public FormatItem
    {
    public:
        void format(std::ostream &os, const Message &message)
        {
            os << message._file_line;
        }
    };
    // 获取线程ID
    class ThreadIdFormatItem : public FormatItem
    {
    public:
        void format(std::ostream &os, const Message &message)
        {
            os << message._thread_id;
        }
    };
    // 获取日志等级
    class LevelFormatItem : public FormatItem
    {
    public:
        void format(std::ostream &os, const Message &message)
        {
            os << LogLevel::levelToString(message._level);
        }
    };
    // 获取有效日志数据
    class PayloadFormatItem : public FormatItem
    {
    public:
        void format(std::ostream &os, const Message &message)
        {
            os << message._payload;
        }
    };
    // 获取日志器名
    class LoggerFormatItem : public FormatItem
    {
    public:
        void format(std::ostream &os, const Message &message)
        {
            os << message._logger;
        }
    };
    // 制表符缩进
    class TabFormatItem : public FormatItem
    {
    public:
        void format(std::ostream &os, const Message &message)
        {
            os << "\t";
        }
    };
    // 换行
    class NewLineFormatItem : public FormatItem
    {
    public:
        void format(std::ostream &os, const Message &message)
        {
            os << "\n";
        }
    };
    // 非格式化的原始字符串
    class OtherFormatItem : public FormatItem           // 其他，非格式化字符
    {
    public:
        OtherFormatItem(const std::string &str) : _str(str) {}      // 数据没有保存在Message对象内，需要使用者自己传入
        void format(std::ostream &os, const Message &message)
        {
            os << _str;
        }

    private:
        std::string _str;       // 存储对应的字符串
    };

    // 对应格式化规则

    /*
        %d 日期
        %T 缩进
        %t 线程id
        %p 日志级别
        %c 日志器名称
        %f 文件名
        %l 行号
        %m 日志消息
        %n 换行
    */

    // 格式化类
    class Formatter
    {
    public:
        using ptr = std::shared_ptr<Formatter>;             // 添加，方便使用
        Formatter(const std::string &pattern = "[%d{%H:%M:%S}][%t][%c][%p][%f:%l]%T%m%n")
            : _pattern(pattern)
        {
            assert(parsePattern());         // 解析操作得是正确的，否则后续操作无法正常执行
        }
        // 获取_pattern
        const std::string &getPattern()
        {
            return _pattern;
        }
        std::string format(const Message &message)      // 外部调用，返回对应格式化后的消息
        {
            std::stringstream ss;
            format(ss, message);
            return ss.str();
        }
        void format(std::ostream &os, const Message &message)
        {
            for (auto &it : _items)             // 对对应的格式调用对应的类中的对应格式化函数
                it->format(os, message);
        }

    private:
        // 示例格式化格式
        // abc%%d[%d{%H:%M:%S}][%t][%c][%f:%l]%T%m%n
        // [%d{%H:%M:%S}][%t][%c][%f:%l]%T%m%n
        // 对格式进行解析
        bool parsePattern()
        {
            std::string key = "", val = "";
            int pos = 0;        // 记录解析位置
            while (pos < _pattern.size())
            {
                if (_pattern[pos] != '%')       // 不是格式化规则，可能是需要输出原字符串
                {
                    val.push_back(_pattern[pos++]);         // 方便调用createFormatItem函数时传参
                    continue;
                }
                // 出现了'%'，可能时格式化规则开始了，也有可能的两个'%'，即转义字符，需要输出一个'%'
                if (!val.empty())           // 先做完将它加入到_item的操作后，再处理其他，否则会出错
                {
                    _items.push_back(createFormatItem(key, val));       // 先将原字符串的格式化子项对象弄完
                    val.clear();            // val的内容已经用完了，清楚，方便下次操作
                }
                ++pos;              // 移至%的下一个字符
                if (pos < _pattern.size() && _pattern[pos] == '%')
                {
                    val.push_back(_pattern[pos++]);             // 两个'%'，即转义字符，需要输出一个'%'，即还是原字符串输出
                    continue;
                }

                // 格式化规则开始
                if (pos < _pattern.size())
                    key.push_back(_pattern[pos++]);         // 规则，即格式'%'后的值。即当其是'%d'时，获取d
                if (pos < _pattern.size() && _pattern[pos] == '{')          // 格式的子格式，如[%d{%H:%M:%S}]
                {
                    ++pos;          // 跳过'{'
                    while (pos < _pattern.size() && _pattern[pos] != '}')           // 寻找子格式的结束符，即'}'
                    {
                        val.push_back(_pattern[pos++]);             // 子格式
                    }
                    if (pos == _pattern.size())             // 找完了都没有找到'}'，子格式的规则错误，即没有正常结束
                    {
                        std::cout << "parsePattern, no '}' after '{', error\n";
                        return false;
                    }
                    ++pos;          // pos自增，跳过'}'
                }
                _items.push_back(createFormatItem(key, val));           // 一个格式规则结束了
                key.clear();        // 方便下次操作
                val.clear();
                // ++pos;           // 不要加，[%d{%H:%M:%S}][%t][%c][%f:%l]%T%m%n，否则当t处理完后，因为前面已经自增了，这里再次自增会漏掉']'
            }
            return true;
        }

        // 构建对应的格式化子项目的对象
        FormatItem::ptr createFormatItem(const std::string &key, const std::string &values)
        {
            // int x = 0;       // 方便调式
            if (key == "d")    return std::make_shared<TimeFormatItem>();
            if (key == "t")    return std::make_shared<ThreadIdFormatItem>();
            if (key == "p")    return std::make_shared<LevelFormatItem>();
            if (key == "c")    return std::make_shared<LoggerFormatItem>();
            if (key == "f")    return std::make_shared<FileNameFormatItem>();
            if (key == "l")    return std::make_shared<FileLineFormatItem>();
            if (key == "T")    return std::make_shared<TabFormatItem>();
            if (key == "n")    return std::make_shared<NewLineFormatItem>();
            if (key == "m")    return std::make_shared<PayloadFormatItem>();        // 当key等于m时，是输出其消息，不要忘了这一情况
            if (key == "")    return std::make_shared<OtherFormatItem>(values);     // 参数需要传递给其构造函数
            std::cout << "createFormatItem, % 后规则符号错误, key: " << values << std::endl;        // 对应规则都不符合，那就是错误的规则输入
            // int y = 0;       // 方便调式
            abort();        // 输入了错误（代码中没有定义）的规则，直接程序报错
        }

    private:
        std::string _pattern;           // 保存日志输出的格式
        std::vector<FormatItem::ptr> _items;    // 格式子项目的对象集合，FormatItem::ptr可调用format生成。即保存格式化字符串对应的子格式化对象
    };
}

#endif